/*
	Copyright (c) 2009-2011, Stefan Webb.

	This file is part of SimpleML. Please read the file "license.txt", 
	which contains the conditions under which this software is distributed.
*/

/*!
	\file file.cpp
	\author Stefan Webb
	\brief Definition of File class members
*/

#include "stdafx.h"

// Default constructor
File::File()
{
	// Initialize variables
	initialize();
}

// Constructor with filename passed
File::File(const wchar_t* filename, FileMode mode)
{
	// Initialize variables
	initialize();

	// Open file
	open(filename, mode);
}

// Default destructor
File::~File()
{
	// Close file
	close();
}

// Initialize variables
void File::initialize()
{
	filename_ = 0;
	mode_ = NOTSET;
	size_ = 0;
}

// Reads part of file into memory as binary
unsigned char* File::read(unsigned int size)
{
	// Make sure file is open
	if (!isReady()) {
		(error << L"No file is open.").warning();
		return 0;
	}

	// Make buffer
	unsigned char* buffer = new unsigned char[size];

	// Read file contents
	bool result = read(size, (char*) buffer);

	// Check for error
	if (result == false) {
		delete [] buffer;
		return 0;
	}

	// Return buffer	
	return buffer;
}

// Reads entire file into memory as binary
unsigned char* File::read()
{
	return read(size());
}

// Reads part of file into specified buffer as binary
bool File::read(unsigned int size, char* buffer)
{
	// Make sure file is open
	if (!isReady()) {
		(error << L"No file is open.").warning();
		return false;
	}

	// Check that mode is read
	if (mode_ != READ) {
		(error << L"File " << filename_ << " was not opened for input").warning();
		return false;
	}

	// Read data as a block:
	unsigned int bytesRead;
	if (!ReadFile(file_, buffer, size, (LPDWORD) &bytesRead, NULL) || bytesRead != size) {
		(error << L"Error reading file " << filename_).warning();
		close();
		return false;
	}

	// Return buffer	
	return true;
}

/* Save block of memory to a file */
bool File::save(unsigned int length, unsigned char* memory)
{
	// Check that file is open
	if (!isReady()) {
		(error << L"No file is open.").warning();
		return false;
	}

	// Check that mode is output
	if (mode_ != WRITE) {
		(error << L"File " << filename_ << " was not opened for output").warning();
		return false;
	}

	// Write to file
	unsigned int bytesWritten;
	if (!WriteFile(file_, memory, length, (LPDWORD) &bytesWritten, NULL) || bytesWritten != length) {
		(error << L"Error writing to file '" << filename_ << "'").warning();
		close();
		return false;
	}

	return true;
}

/* Get size of file. */
unsigned int File::size()
{
	// Check that file is open
	if (!isReady()) {
		(error << L"No file is open.").warning();
		return 0;
	}

	// If have already calculated length return length
	if (size_ && mode_ == READ)
		return size_;

	// Get length of file
	LARGE_INTEGER largeInteger;
	if (!GetFileSizeEx(file_, &largeInteger)) {
		(error << L"Could not get file size.").warning();
		close();
		return 0;
	}

	// Check that it's not too big
	if (largeInteger.HighPart != 0) {
		(error << L"File exceeds 4GB limit.").warning();
		close();
		return 0;
	}

	// Otherwise set and return size
	size_ = largeInteger.LowPart;
	return size_;
}

// Opens file
bool File::open(const wchar_t* filename, FileMode mode)
{
	// Close previously open file
	if (filename_)
		close();

	// Set mode
	unsigned int mode2;
	if (mode == READ)
		mode2 = GENERIC_READ;
	else if (mode == WRITE)
		mode2 = GENERIC_WRITE;
	else {
		(error << "File " << filename << " opened with invalid mode.").warning();
		return false;
	}

	// Store mode and filename
	mode_ = mode;
	unsigned int length = wcslen(filename);
	filename_ = new wchar_t[length + 1];
	memcpy(filename_, filename, (length + 1) * 2);

	// Try to open file
	//file_.open(filename, mode2);
	file_ = CreateFile(filename, mode2, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 
						OPEN_EXISTING, NULL, NULL);

	// In the future may want to do GetLastError for more information.
	if(file_ == INVALID_HANDLE_VALUE) {
		(error << "Could not open file " << filename << ".").warning();
		return false;
	}

	// Set state to true and return true
	setState(true);
	return true;
}

// Closes file
bool File::close()
{
	if (isReady()) {
		// Set state to false
		setState(false);

		// Close file and free memory to filename string
		CloseHandle(file_);
		delete [] filename_;
		
		// Initialize variables
		initialize();
		return true;
	}
	
	//(error << "There is no file to close.").warning();
	return false;
}

// Compare two file names, which is a case insensitive string compare on Windows
bool File::compare(const wchar_t* filename1, const wchar_t* filename2)
{
	// Perform case insensitive string compare on filenames
	if (!strcasecmp(filename1, filename2))
		return true;
	else
		return false;
}

// Case insensitive string compare
int File::strcasecmp(const wchar_t *str1, const wchar_t *str2)
{
	// Handle if string pointers are zero
	if (!str1 && !str2)
		return 0;
  if (!str1)
		return 1;
	if (!str2)
		return -1;

	// Loop while strings are equal and before reached zero terminater
  while (*str1 && *str2 && tolower(*str1) == tolower(*str2)) {
			str1++;
			str2++;
  }

	// Return result
  return tolower((wchar_t)*str1) - tolower((wchar_t)*str2);
}